/*
 * Copyright [2018] [Alex/libo(liboware@gmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.apidoc.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hyts.apidoc.handler.TemplateDataHandler;
import com.hyts.apidoc.model.ApiDefination;
import com.hyts.apidoc.springboot.ApiDocConfiguration;
import com.hyts.apidoc.handler.TemplateInitHandler;
import com.hyts.apidoc.model.ApiDoc;
import com.hyts.apidoc.utility.StringUtils;
import com.hyts.template.freemarker.FreemarkerContext;
import com.hyts.template.freemarker.FreemarkerStream;

/**
 * @author LiBo/Alex
 * @version V1.0
 */
public class ApiDocServlet extends HttpServlet{


    /**
     * api doc scan package
     */
    private String apiDocPackage;

    /**
     * @fieldName serialVersionUID
     * @fieldType long
     */
    private static final long serialVersionUID = -7985712981033737556L;

    //private static final String DEFAULT_API_DOC_SERVLET_PACKAGE_KEY = "apiScanPackage";

    static List<ApiDoc> apiDocList = null;

    private static final String DEFAULT_ROOT_PACKAGE_TOKEN = "_(根包root)";

    /**
     * data index page
     */
    private static final String DEFAULT_API_SERVLET_INDEX_PATH = "/index";

    /**
     * data detail page
     */
    private static final String DEFAULT_API_SERVLET_DATA_PATH = "/detail";

    /**
     * data list page
     */
    private static final String DEFAULT_API_SERVLET_LIST_PATH = "/list";


    private static final String DEFAULT_METHOD_SERVLET_LIST_PATH = "/methodlist";


    private static final String DEFAULT_API_TEMPLATE_BASE_PATH = "/META-INF/resources/apidoc/";


    public static final String[] DEFAULT_SERVLET_MAPPINGS =
                    {"/apidoc/index","/apidoc/detail","/apidoc/list","/apidoc/methodlist"};


    public static final int DEFAULT_PAGE_SIZE = 10;


    public static final int DEFAULT_PAGE_INDEX = 6;

    /**
     * @fieldName printer
     * @fieldType FreemarkerStream<Map<String,Object>>
     */
    FreemarkerStream<Map<String,Object>> printer = null;

    /**
     * @fieldName dataMap
     * @fieldType Map<String,List<ApiDoc>>
     */
    private static Map<String,List<ApiDoc>> dataMap = null;


    private static Map<String,List<ApiDefination>> methodMap = null;



    @Override
    public void init() throws ServletException {
        String apiDocPackageName = apiDocPackage;
        //getServletConfig().getInitParameter(DEFAULT_API_DOC_SERVLET_PACKAGE_KEY);
        if(dataMap == null || dataMap.isEmpty()) {
            TemplateInitHandler.init(apiDocPackageName);
        }
        if(methodMap == null){
            methodMap = Maps.newHashMap();
        }
        printer = new FreemarkerStream<Map<String,Object>>(new FreemarkerContext()
                .createContext(ApiDocServlet.class,DEFAULT_API_TEMPLATE_BASE_PATH));
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        //request 请求操作
        if(req.getRequestURI().indexOf(DEFAULT_API_SERVLET_INDEX_PATH) > -1 ) {
            index(req,resp);
        }else if(req.getRequestURI().indexOf(DEFAULT_API_SERVLET_DATA_PATH)>-1) {
            detail(req,resp);
        }else if(req.getRequestURI().indexOf(DEFAULT_API_SERVLET_LIST_PATH)>-1) {
            list(req,resp);
        }else if(req.getRequestURI().indexOf(DEFAULT_METHOD_SERVLET_LIST_PATH)>-1){
            methodList(req,resp);
        }else {
            req.getRequestDispatcher(req.getRequestURI()).forward(req, resp);
        }
    }

    /**
     * @throws IOException
     * @param req
     * @param resp
     * @exception
     */
    public void index(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HashMap<String,Object> param = new HashMap<>();
        param.put("contextPath", req.getContextPath());
        dataMap = TemplateDataHandler.countApiDocList();
        List<String> dataList =  new ArrayList<>(dataMap.keySet());
        dataList = dataList.parallelStream().map(param2->{
            if(ApiDocConfiguration.annotationBasePackage.equals(param2)) {
                param2 = DEFAULT_ROOT_PACKAGE_TOKEN;
            }else {
                param2 = param2.replace(ApiDocConfiguration.annotationBasePackage+".","");
            }
            return param2;
        }).sorted().collect(Collectors.toList());;
        param.put("packageBase",ApiDocConfiguration.annotationBasePackage);
        param.put("packageList", dataList);
        param.put("packageName",ApiDocConfiguration.annotationBasePackage+"."+dataList.get(0));
        printer.writer("dataIndex.html",param,resp.getWriter());
    }

    /**
     * @throws IOException
     * @param req
     * @param resp
     * @exception
     */
    public void detail(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HashMap<String,Object> param = new HashMap<>();
        String packageName = req.getParameter("packageName");
        if(!packageName.startsWith(ApiDocConfiguration.annotationBasePackage)) {
            packageName = ApiDocConfiguration.annotationBasePackage+"."+packageName;
        }
        List<ApiDoc> dataNum = dataMap.get(packageName);
        if(dataNum == null) {
            dataNum = dataMap.get(ApiDocConfiguration.annotationBasePackage);
            packageName = packageName.replace("."+DEFAULT_ROOT_PACKAGE_TOKEN, "");
        }
        param.put("packageName",packageName);
        param.put("fileNum",dataNum.size());
        param.put("author","暂未开放此功能");
        param.put("createDate","暂未开放此功能");
        param.put("lastModifiedDate","暂未开放此功能");
        param.put("description","暂未开放此功能");
        param.put("projectName",req.getContextPath());
        param.put("contextPath", req.getContextPath());
        printer.writer("dataInfo.html",param,resp.getWriter());
    }


    /**
     * @throws IOException
     * @param req
     * @param resp
     * @exception
     */
    public void list(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        HashMap<String,Object> param = new HashMap<>();
        param.put("contextPath", req.getContextPath());
        String packageName = req.getParameter("packageName");
        if(!packageName.startsWith(ApiDocConfiguration.annotationBasePackage)) {
            packageName = ApiDocConfiguration.annotationBasePackage+"."+packageName;
        }
        int pageIndex = StringUtils.isBlank(req.getParameter("pageIndex"))?1:
            Integer.parseInt(req.getParameter("pageIndex"));
        if(packageName.endsWith(DEFAULT_ROOT_PACKAGE_TOKEN)) {
            packageName = packageName.replace("."+DEFAULT_ROOT_PACKAGE_TOKEN,"");
        }
        List<ApiDoc> dataList = TemplateDataHandler.getClassDataListByPackageName(packageName);
        //构建method对象的数据集合
        if(dataList != null) {
            for(ApiDoc param3:dataList){
                if(param3 != null) {
                    String key = param3.getPackageName() + "." + param3.getApiTypeName();
                    if (!methodMap.containsKey(key)) {
                        methodMap.put(key, param3.getApiDefinationList() == null?
                            Lists.newArrayList(new ApiDefination()):param3.getApiDefinationList() );
                    }
                }
            }
        }
        int pageNum = TemplateDataHandler.pageNum(dataList);
        List<Integer> pageList = TemplateDataHandler.getPageIndexList(pageNum,pageIndex);
        param.put("pageIndexList",pageList);
        param.put("classList", TemplateDataHandler.getListByPageApidDoc(dataList, pageIndex));
        param.put("packageName",packageName);
        printer.writer("dataList.html",param,resp.getWriter());
    }


    public void methodList(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        HashMap<String,Object> param = new HashMap<>();
        param.put("contextPath", req.getContextPath());
        //获取包名和类名称->全限制定义名称
        String packageName = req.getParameter("packageName");
        String className = req.getParameter("className");
        //获取分页下标
        int pageIndex = StringUtils.isBlank(req.getParameter("pageIndex"))?1:
                Integer.parseInt(req.getParameter("pageIndex"));
        List<ApiDefination> dataList = methodMap.get(packageName+"."+className);
        int pageNum = TemplateDataHandler.pageNum(dataList);
        List<Integer> pageList = TemplateDataHandler.getPageIndexList(pageNum,pageIndex);
        param.put("pageIndexList",pageList);
        param.put("packageName",packageName);
        param.put("className",className);
        param.put("methodList", TemplateDataHandler.getListByPage(dataList, pageIndex));
        printer.writer("dataMethodList.html",param,resp.getWriter());
    }


    public ApiDocServlet setApiDocPackage(String apiDocPackage) {
        this.apiDocPackage = apiDocPackage;
        return this;
    }

    public static List<ApiDoc> getApiDocList() {
        return apiDocList;
    }

    public static void setApiDocList(List<ApiDoc> apiDocList) {
        ApiDocServlet.apiDocList = apiDocList;
    }


}
